package main

import (
	"./concat"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"
)

const (
	DefaultPrefix = "?"
	DefaultSep    = ","
)

var (
	runAddr      string
	cacheFolder  string
	cacheExpire  time.Duration
	staticFolder string
	unique       bool
	pattern      string
	serverInfo   string
	compatible   bool

	ErrInvalidRequest = errors.New("invalid request")
	ErrIllegalRequest = errors.New("illegal request")
)

func init() {
	flag.StringVar(&serverInfo, "s", "nginx/1.2.9", "头文件服务信息")
	flag.StringVar(&runAddr, "a", ":35510", "运行地址")
	flag.StringVar(&cacheFolder, "cf", "", "缓存文件夹地址")
	flag.DurationVar(&cacheExpire, "ce", time.Second*10, "缓存过期时间")
	flag.StringVar(&staticFolder, "sf", "", "静态文件夹地址")
	flag.BoolVar(&unique, "u", true, "是否只接受同一类型文件")
	flag.BoolVar(&compatible, "c", true, "是否处理多余的.为/")
	flag.StringVar(&pattern, "p", "/merge", "http请求匹配模式; 默认 http://127.0.0.1/merge??js.jquery.js")
	flag.Parse()
	concat.MkdirAll(cacheFolder)
}

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

// 获取错误信息，模拟nginx错误页面
func getErrorContent(code int, content string) []byte {
	patten := `<html>
    <head><title>%d %s</title></head>
    <body bgcolor="white">
    <center><h1>%d %s</h1></center>
    <hr><center>nginx</center>
    </body>
    </html>`
	return []byte(fmt.Sprintf(patten, code, content, code, content))
}

func errorHandler(code int, content string) func(http.ResponseWriter, *http.Request) {
	return func(rw http.ResponseWriter, req *http.Request) {
		if serverInfo != "" {
			rw.Header().Set("Server", serverInfo)
		}
		contents := getErrorContent(code, content)
		rw.Write(contents)
	}

}

// 文件连接处理
func concatHandle(rw http.ResponseWriter, req *http.Request) {
	rawQuery := req.URL.RawQuery
	if serverInfo != "" {
		rw.Header().Set("Server", serverInfo)
	}
	rw.Header().Set("Content-Type", concat.MIMEType(rawQuery, DefaultSep))

	defer func() {
		if x := recover(); x != nil {
			log.Printf("an error occurred. info: %s", x)
			rw.Header().Set("Content-Type", "text/html; charset=utf-8")
			contents := getErrorContent(http.StatusBadRequest, "Bad Request")
			rw.Write(contents)
		}
	}()

	// 如果用户输入..要非法访问，抛出错误
	if strings.Contains(rawQuery, "..") {
		panic(ErrIllegalRequest)
	}

	if strings.HasPrefix(rawQuery, DefaultPrefix) {
		var contents []byte
		var err error
		rawQuery = rawQuery[len(DefaultPrefix):]

		if cacheFolder != "" {
			cache := concat.NewFileCache(cacheFolder, cacheExpire)
			contents, err = cache.Get(rawQuery)
			if err != nil {
				concat, err := concat.NewConcatFile(staticFolder, rawQuery, DefaultSep, unique, compatible)
				checkError(err)
				contents, err = concat.Content()
				checkError(err)
				err = cache.Set(rawQuery, contents)
				checkError(err)
			}
		} else {
			concat, err := concat.NewConcatFile(staticFolder, rawQuery, DefaultSep, unique, compatible)
			checkError(err)
			contents, err = concat.Content()
		}
		rw.Write(contents)
		return
	}

	panic(ErrInvalidRequest)
}

func main() {
	http.HandleFunc("/", errorHandler(http.StatusForbidden, "Forbidden"))
	http.HandleFunc(pattern, concatHandle)
	if err := http.ListenAndServe(":35510", nil); err != nil {
		log.Fatal(err)
	}
}
